#include "../corelib/event/event_queue.hh"
#include "../corelib/event/event_listener.hh"
#include "../corelib/event/event_object.hh"
#include "unittest/unittest.hh"
#include <chrono>

using namespace kratos::corelib;

FIXTURE_BEGIN(EventQueue)

class MyListener : public EventListener {
public:
    MyListener(std::uint64_t id)
    : done(false) {
        myID = id;
    }
    virtual bool handleEvent(EventObject* eo) {
        done = true;
        return true;
    }
    virtual std::uint64_t getID() {
        return myID;
    }

public:
    bool done;
    std::uint64_t myID;
};


class MyEvent : public EventObject {
public:
    MyEvent() {
        setEventID(1);
    }
    MyEvent(std::uint64_t receiver) {
        setEventID(1);
        setReceiver(receiver);
    }
    virtual ~MyEvent() {};
};

CASE(TestAddListener) {
    EventQueue q;
    ASSERT_FALSE(q.addListener(0, nullptr));
    MyListener listener(1);
    ASSERT_TRUE(q.addListener(0, &listener));
}

CASE(TestGetListener) {
    EventQueue q;
    MyListener listener(1);
    ASSERT_TRUE(q.addListener(0, &listener));
    ASSERT_TRUE(&listener == q.getListener(1));
}

CASE(TestGetListeners) {
    EventQueue q;
    MyListener listener1(1);
    MyListener listener2(2);
    ASSERT_TRUE(q.addListener(1, &listener1));
    ASSERT_TRUE(q.addListener(1, &listener2));
    ASSERT_TRUE(q.getListeners(1).size() == 2);
}

CASE(TestRemoveListener) {
    EventQueue q;
    MyListener listener1(1);
    ASSERT_TRUE(q.addListener(1, &listener1));
    ASSERT_TRUE(q.removeListener(1, &listener1));
}

CASE(TestRemoveAllListener) {
    EventQueue q;
    MyListener listener1(1);
    MyListener listener2(2);
    ASSERT_TRUE(q.addListener(1, &listener1));
    ASSERT_TRUE(q.addListener(1, &listener2));
    ASSERT_TRUE(q.removeAllListener(1));
    ASSERT_TRUE(q.getListeners(1).size() == 0);
}

CASE(TestfireEventNow) {
    EventQueue q;
    MyListener listener(1);
    q.addListener(1, &listener);
    q.fireEventNow<MyEvent>();
    ASSERT_TRUE(listener.done);
    listener.done = false;

    MyListener listener1(1);
    q.addListener(2, &listener1);
    q.fireEventNow<MyEvent>(1);
    ASSERT_TRUE(!listener.done);
    ASSERT_TRUE(listener1.done);
}

CASE(TestfireEventLater) {
    EventQueue q;
    MyListener listener(1);
    q.addListener(1, &listener);
    q.fireEventLater<MyEvent>(1000);
    ASSERT_FALSE(q.fireEventLater<MyEvent>(0));
    std::this_thread::sleep_for(std::chrono::milliseconds(1100));
    q.update();
    ASSERT_TRUE(listener.done);
}

CASE(TestFireEventPeriod) {
    EventQueue q;
    MyListener listener(1);
    q.addListener(1, &listener);
    q.fireEventPeriod<MyEvent>(1000);
    std::this_thread::sleep_for(std::chrono::milliseconds(1100));
    q.update();
    ASSERT_TRUE(listener.done);
    listener.done = false;
    std::this_thread::sleep_for(std::chrono::milliseconds(1100));
    q.update();
    ASSERT_TRUE(listener.done);
}

FIXTURE_END(EventQueue)
